import "@typespec/http";
import "@typespec/openapi3";

using TypeSpec.Http;
using TypeSpec.OpenAPI;

namespace OpenMeter;

/**
 * A Problem Details object (RFC 7807).
 * Additional properties specific to the problem type may be present.
 */
@error
@extension("x-go-type", "models.StatusProblem")
@extension(
  "x-go-type-import",
  #{ path: "github.com/openmeterio/openmeter/pkg/models" }
)
@friendlyName("UnexpectedProblemResponse")
model Error {
  @header contentType: "application/problem+json";

  /**
   * Type contains a URI that identifies the problem type.
   */
  @example("about:blank")
  type: url = "about:blank";

  /**
   * A a short, human-readable summary of the problem type.
   */
  @example("Bad Request")
  title: string;

  /**
   * The HTTP status code generated by the origin server for this occurrence of the problem.
   */
  @minValue(400)
  @maxValue(599)
  @example(400)
  status?: int16;

  /**
   * A human-readable explanation specific to this occurrence of the problem.
   */
  @example("The request body must be a JSON object.")
  detail: string;

  /**
   * A URI reference that identifies the specific occurrence of the problem.
   */
  @example("urn:request:local/JMOlctsKV8-000001")
  instance: url;

  /**
   * Additional properties specific to the problem type may be present.
   */
  @example(#{
    validationErrors: #[
      #{ code: "validation_error", message: "Validation error" }
    ],
    otherAttribute: "otherValue",
  })
  extensions?: Record<unknown>;

  /**
   * Additional properties specific to the problem type may be present.
   */
  ...Record<unknown>;
}

alias CommonErrorsWithValidation =
  | ValidationErrorResponse
  | UnauthorizedError
  | ForbiddenError
  | InternalServerErrorError
  | ServiceUnavailableError
  | PreconditionFailedError
  | Error;

alias CommonErrors =
  | BadRequestError
  | UnauthorizedError
  | ForbiddenError
  | InternalServerErrorError
  | ServiceUnavailableError
  | PreconditionFailedError
  | Error;

/**
 * The server cannot or will not process the request due to something that is perceived to be a client error (e.g., malformed request syntax, invalid request message framing, or deceptive request routing).
 */
@error
@friendlyName("BadRequestProblemResponse")
model BadRequestError extends Error {
  @statusCode _: 400;
}

/**
 * A BadRequestError with a validationErrors extension.
 */
@error
@friendlyName("ValidationErrorProblemResponse")
model ValidationErrorResponse {
  ...OmitProperties<BadRequestError, "extensions">;

  /**
   * Validation issues.
   */
  @visibility(Lifecycle.Read)
  extensions?: {
    validationErrors?: ValidationError[];
  };
}

/**
 * Validation errors providing detailed description of the issue.
 */
@friendlyName("ValidationError")
model ValidationError {
  /**
   * The path to the field.
   */
  @visibility(Lifecycle.Read)
  @example("addons/pro/ratecards/token/featureKey")
  field: string;

  /**
   * The machine readable description of the error.
   */
  @visibility(Lifecycle.Read)
  @example("invalid_feature_key")
  code: string;

  /**
   * The human readable description of the error.
   */
  @visibility(Lifecycle.Read)
  @example("not found feature by key")
  message: string;

  /**
   * Additional attributes.
   */
  @visibility(Lifecycle.Read)
  attributes?: Annotations;
}

/**
 * Generic ErrorExtension as part of HTTPProblem.Extensions.[StatusCode]
 */
@friendlyName("ErrorExtension")
model ErrorExtension {
  /**
   * The path to the field.
   */
  @visibility(Lifecycle.Read)
  @example("addons/pro/ratecards/token/featureKey")
  field: string;

  /**
   * The machine readable description of the error.
   */
  @visibility(Lifecycle.Read)
  @example("invalid_feature_key")
  code: string;

  /**
   * The human readable description of the error.
   */
  @visibility(Lifecycle.Read)
  @example("not found feature by key")
  message: string;

  ...Record<unknown>;
}

/**
 * The request has not been applied because it lacks valid authentication credentials for the target resource.
 */
@error
@friendlyName("UnauthorizedProblemResponse")
model UnauthorizedError extends Error {
  @statusCode _: 401;
}

/**
 * The server understood the request but refuses to authorize it.
 */
@error
@friendlyName("ForbiddenProblemResponse")
model ForbiddenError extends Error {
  @statusCode _: 403;
}

/**
 * The origin server did not find a current representation for the target resource or is not willing to disclose that one exists.
 */
@error
@friendlyName("NotFoundProblemResponse")
model NotFoundError extends Error {
  @statusCode _: 404;
}

/**
 * The request could not be completed due to a conflict with the current state of the target resource.
 */
@error
@friendlyName("ConflictProblemResponse")
model ConflictError extends Error {
  @statusCode _: 409;
}

/**
 * One or more conditions given in the request header fields evaluated to false when tested on the server.
 */
@error
@friendlyName("PreconditionFailedProblemResponse")
model PreconditionFailedError extends Error {
  @statusCode _: 412;
}

/**
 * The server encountered an unexpected condition that prevented it from fulfilling the request.
 */
@error
@friendlyName("InternalServerErrorProblemResponse")
model InternalServerErrorError extends Error {
  @statusCode _: 500;
}

/**
 * The server does not support the functionality required to fulfill the request.
 */
@error
@friendlyName("NotImplementedProblemResponse")
model NotImplementedError extends Error {
  @statusCode _: 501;
}

/**
 * The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.
 */
@error
@friendlyName("ServiceUnavailableProblemResponse")
model ServiceUnavailableError extends Error {
  @statusCode _: 503;
}

/**
 * The server, while acting as a gateway or proxy, did not receive a timely response from an upstream server it needed to access in order to complete the request.
 */
@error
@friendlyName("GatewayTimeoutProblemResponse")
model GatewayTimeoutError extends Error {
  @statusCode _: 504;
}
